package com.superyee.commonlib.widgets.dragView;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;

import java.lang.ref.WeakReference;

public class DragTouchListener implements View.OnTouchListener {
    private Rect mBoundsInScreen;
    private float mPreviousX;
    private float mPreviousY;
    private boolean mHasMoved;
    private int mTouchSlop;
    private int mEdgePaddingLeft;
    private int mEdgePaddingRight;
    private int mEdgePaddingTop;
    private int mEdgePaddingBottom;
    public View mFloatView;
    private ViewGroup parentView, disableView;
    private FloatButtonCallback mFloatButtonCallback;
    private int mDownPointerId;
    private Interpolator mInterpolator;
    private FloatAnimatorUpdateListener mUpdateListener;

    public DragTouchListener(Context context, ViewGroup rootView, View floatView, int edgePadding) {
        this(context, rootView, null, floatView, edgePadding);
    }

    public DragTouchListener(Context context, final ViewGroup rootView, ViewGroup disableView, View floatView, int edgePadding) {
        mBoundsInScreen = new Rect();
        mFloatView = floatView;
        this.disableView = disableView;
        mInterpolator = new DecelerateInterpolator();
        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mEdgePaddingBottom = edgePadding;
        mEdgePaddingLeft = edgePadding;
        mEdgePaddingRight = edgePadding;
        mEdgePaddingTop = edgePadding;
        parentView = rootView;
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                mBoundsInScreen.set(0,
                        0,
                        rootView.getWidth() - mFloatView.getWidth(),
                        rootView.getHeight() - mFloatView.getHeight());
            }
        });
    }

    public void setBounds(Rect rect) {
        mBoundsInScreen = rect;
    }


    public void setMargin(int marginleft, int margintop, int marginright, int marginbottom) {
        mEdgePaddingBottom = marginbottom;
        mEdgePaddingLeft = marginleft;
        mEdgePaddingRight = marginright;
        mEdgePaddingTop = margintop;
    }


    /**
     * 调整floatview布局
     *
     * @param event
     * @return
     */
    private boolean adjustMarginParams(MotionEvent event) {
        float x = event.getRawX();
        float y = event.getRawY();
        float deltaX = x - mPreviousX;
        float deltaY = y - mPreviousY;
        if (!mHasMoved) {
            if (Math.abs(deltaX) < mTouchSlop && Math.abs(deltaY) < mTouchSlop) {
                return false;
            }
        }
        mPreviousX = x;
        mPreviousY = y;
        float newX = mFloatView.getX() + deltaX;
        float newY = mFloatView.getY() + deltaY;
        newX = Math.max(newX, mBoundsInScreen.left + mEdgePaddingLeft);
        newX = Math.min(newX, mBoundsInScreen.right - mEdgePaddingRight);
        newY = Math.max(newY, mBoundsInScreen.top + mEdgePaddingTop);
        newY = Math.min(newY, mBoundsInScreen.bottom - mEdgePaddingBottom);
        mFloatView.setX(newX);
        mFloatView.setY(newY);
        return true;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getActionMasked();
        if (mFloatButtonCallback != null) {
            mFloatButtonCallback.onTouch();
        }
        boolean result = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                if (disableView != null) {
                    disableView.requestDisallowInterceptTouchEvent(true);
                }
                parentView.requestDisallowInterceptTouchEvent(true);
                mDownPointerId = event.getPointerId(0);
                mPreviousX = event.getRawX();
                mPreviousY = event.getRawY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                result = true;
                if (mDownPointerId >= 0) {
                    int index = event.getActionIndex();
                    int id = event.getPointerId(index);
                    if (id == mDownPointerId) {
                        boolean update = adjustMarginParams(event);
                        if (!update) {
                            break;
                        }
                        mFloatView.requestLayout();
                        mHasMoved = true;
                        result = true;
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                parentView.requestDisallowInterceptTouchEvent(false);
                if (disableView != null) {
                    disableView.requestDisallowInterceptTouchEvent(false);
                }
                if (mDownPointerId >= 0 && mHasMoved) {
                    event.setAction(MotionEvent.ACTION_CANCEL);
                    adjustMarginParams(event);
                    mFloatView.requestLayout();
                    int center = (mBoundsInScreen.width() - mFloatView.getWidth()) / 2;
                    float x = mFloatView.getX();
                    int destX = 0;
                    int posX = Gravity.LEFT;
                    //抬起时 根据位置强制把浮动按钮归于左边或右边
                    if (x < center) { // 左边
                        destX = mBoundsInScreen.left + mEdgePaddingLeft;
                    } else {
                        posX = Gravity.RIGHT;
                        destX = mBoundsInScreen.right - mEdgePaddingRight;
                    }
                    if (mFloatButtonCallback != null) {
                        float posY = 0;
                        if (mBoundsInScreen.height() - mFloatView.getHeight() != 0) {
                            posY = 1f * (mFloatView.getY() - mBoundsInScreen.top) / (mBoundsInScreen.height() - mFloatView.getHeight());
                        }
                        mFloatButtonCallback.onPositionChanged(destX, mFloatView.getY(), posX, posY);
                    }
                    float deltaHorizon = destX - x;
                    //小于100直接移动 否则开启动画
                    if (Math.abs(deltaHorizon) < 100) {
                        mFloatView.setX(destX);
                        mFloatView.requestLayout();
                    } else {
                        ValueAnimator animator = ValueAnimator.ofFloat(x, destX);
                        animator.setInterpolator(mInterpolator);
                        if (mUpdateListener == null) {
                            mUpdateListener = new FloatAnimatorUpdateListener();
                            mUpdateListener.setUpdateView(DragTouchListener.this);
                        }
                        animator.addUpdateListener(mUpdateListener);
                        animator.setDuration(200);
                        animator.start();
                    }
                }
                resetStatus();
                break;
            }
        }
        return result;
    }


    private void resetStatus() {
        mDownPointerId = -1;
        mPreviousX = -1;
        mPreviousY = -1;
        mHasMoved = false;
    }

    public void setFloatButtonCallback(FloatButtonCallback floatButtonCallback) {
        this.mFloatButtonCallback = floatButtonCallback;
    }


    private static class FloatAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener {

        private WeakReference<DragTouchListener> mListener;

        public void setUpdateView(DragTouchListener listener) {
            mListener = new WeakReference<DragTouchListener>(listener);
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Float value = (Float) animation.getAnimatedValue();
            DragTouchListener listener = null;
            if (mListener == null || (listener = mListener.get()) == null) {
                return;
            }
            listener.mFloatView.setX(value);
            listener.mFloatView.requestLayout();
        }
    }

    /**
     * 触摸监听回调
     */
    public interface FloatButtonCallback {
        void onPositionChanged(float x, float y, float gravityX, float percentY);

        void onTouch();
    }
}
